/* 
CPSC 323 - Symbol Table Handler and Assembly Instructions
Katie Soto
Chelsea Gunderson
12/2011
*/

#ifndef ASSEMBLY_H
#define ASSEMBLY_H

#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <hash_map>
#include <stack>
#include <windows.h> 

using namespace std;

/********ASSEMBLY INSTRUCTIONS:*************

PUSHI   {Integer Value} Pushes the {Integer Value} onto the Top of the Stack (TOS)
PUSHM   {ML - Memory Location} Pushes the value stored at {ML} onto TOS
POPM    {ML} Pops the value from the top of the stack and stores it at {ML}
STDOUT  Pops the value from TOS and outputs it to the standard output
STDIN   Gets the value from the standard input and places it onto TOS
ADD     Pop the first two items from stack and push the sum onto TOS
SUB     Pop the first two items from stack and push the difference 
		onto TOS (Second item - First item)
MUL     Pop the first two items from stack and push the product onto the TOS
DIV     Pop the first two items from stack and push the result onto the 
		TOS (Second item/First item--ignore the remainder)
GRT     Pops two items from stack and pushes 1 onto TOS if the second item 
		is larger, else pushes 0
LES     Pops two items from stack and pushes 1 onto TOS if the second item 
		is smaller, else pushes 0
EQU     Pops two items from stack and pushes 1 onto TOS if the second item 
		is equal, else pushes 0
JUMPZ   {IL - Instruction Location} Pop the stack and if the value is 0, 
		then jump to {IL}
JUMP    {IL} Unconditionally jump to {IL}
LABEL   Empty Instruction; provides the instruction location to jump to

*/      

//********FUNCTIONS & VARIABLES:*************
        void Initialize(); 
  //Symbol Table Functions:     
        bool check_table();     
        void insert_into_SymTable(string identifier, string type);     
        void print_out_SymTable();     
        string get_instr_address(string token);
        string get_id_type(string lexeme);
  //Instruction Table Functions:
        void gen_instr(string instruction, string operand_address);
        void back_patch (string jump_addr);
		void back_patch_special (string jump_addr, string addr_string);
        void print_out_InstrTable();
        string get_current_inst_address();
  //JumpStack Functions:
        void push_jumpstack(string instr_address);
        string pop_jumpstack();
        
        string symbol_Table_Array[300][3];
        string instr_Table_Array[300][3];
        int num_of_entries;
        int instr_addr;
        int memory_address;
        bool is_declared;
        bool identifier_exists;
        stack<string> jumpStack;
        string memory_address_string;
        string memoryLocation;
        string instr_addr_string;
        bool found;
        string id_type;
        
//********FUNCTION DEFINITIONS:*************
//initializes variables to the starting values
void Initialize()
{
    instr_addr = 1;         //instructions start with instruction #1.
    memory_address = 1000;
    is_declared = false;
    identifier_exists = false;
    num_of_entries = 0;
    found=false;
}
//*****Symbol Table Actions:*****
//checks if an identifier is already in the symbol table
bool check_table(string checkIdentifier)
{
    for(int i=0; i<=num_of_entries; i++)
    {
            if(symbol_Table_Array[num_of_entries][0]==checkIdentifier)
            {
                    cout << "Warning: Re-Declaration: The identifier "<<checkIdentifier<<" already exists in the table..." << endl;
                    //just a warning, doesn't need to exit the program.
                    return true;
            }

    }
    //else, return false (identifier not in symbol table yet)
    return false;
}
//inserts an identifier and the type into the symbol table
void insert_into_SymTable(string identifier, string type)
{
    /*
    Identifier  MemoryLocation  Type
    i           1000            integer
    max         1001            integer
    sum         1002            integer
    */
    //convert memory_address to string so we can insert it
            char buf[5];
            memory_address_string = itoa(memory_address, buf, 10);
    //if identifier is not already in the table...
	if(check_table(identifier)==false) 
    {  //then insert into table
            symbol_Table_Array[num_of_entries][0] = identifier;
            symbol_Table_Array[num_of_entries][1] = memory_address_string;
            symbol_Table_Array[num_of_entries][2] = type;
            num_of_entries++;
            memory_address++;
    }
	//if it IS already in the table, then don't re-insert it
}
//prints out the Symbol Table
void print_out_SymTable()
{
    /*
    Identifier  MemoryLocation  Type
    i           1000                        integer
    max                     1001                    integer
    sum                     1002                    integer
    */
    //output file to print to: resultsFile_Asmb
    cout<<"SYMBOL TABLE (list of identifiers): "<<endl;
    resultsFile_Asmb<<"SYMBOL TABLE (list of identifiers): "<<endl;
    cout<<setw(18)<<left<<"Identifier"<<setw(18)<<left<<"Memory Location"<<setw(18)<<left<<"Type"<<endl;
    resultsFile_Asmb<<setw(18)<<left<<"Identifier"<<setw(18)<<left<<"Memory Location"<<setw(18)<<left<<"Type"<<endl;
    cout<<"------------------------------------------------------"<<endl;
    resultsFile_Asmb<<"------------------------------------------------------"<<endl;
    //Print out all identifiers in the table
    for(int y=0; y<=num_of_entries; y++)
    {
            cout<<setw(18)<<left<<symbol_Table_Array[y][0]<<setw(18)<<left<<symbol_Table_Array[y][1]<<setw(18)<<left<<symbol_Table_Array[y][2]<<endl;
            resultsFile_Asmb<<setw(18)<<left<<symbol_Table_Array[y][0]<<setw(18)<<left<<symbol_Table_Array[y][1]<<setw(18)<<left<<symbol_Table_Array[y][2]<<endl;
    }
}
//gets instruction address of a lexeme from the Symbol Table 
string get_instr_address(string lexeme)
{  
    found=false;
    for(int y=0; y<=num_of_entries; y++)
    {
            if(symbol_Table_Array[y][0]==lexeme)
            {
                    //found the lexeme listed in the symbol table. now get that address:
                    memoryLocation = symbol_Table_Array[y][1];
                    found = true;
            }
    }
        
    if(found==true)
    {
        return memoryLocation;
    }else{
        cout<<"Error: variable '"<<lexeme<<"' has not been declared (not found in symbol table)."<<endl;
    //major error, stop program:    
        Sleep(5000);
        exit(0);
        return "error";
    }
}

//get the type of a lexeme from the Symbol Table
string get_id_type(string lexeme)
{
	found=false;
	for(int y=0; y<=num_of_entries; y++)
		{
			if(symbol_Table_Array[y][0]==lexeme)
			{
					//found the lexeme listed in the symbol table. now get that type:
					id_type = symbol_Table_Array[y][2];
					found = true;
			}
		}
        
    if(found==true)
    {
        return id_type;
    }else{
        cout<<"Error: variable '"<<lexeme<<"' has not been declared (not found in symbol table)."<<endl;
    //major error, stop program:    
        Sleep(5000);
        exit(0);
        return "error";
    }
}

//*****Instruction Table Actions:*****
//generate instruction by inserting into the intruction Table
void gen_instr(string instruction, string operand_address)
{       
	//convert instr_addr to string so we can insert it
	char buf2[3];
	instr_addr_string = itoa(instr_addr, buf2, 10);
	instr_Table_Array[instr_addr][0]=instr_addr_string;
	instr_Table_Array[instr_addr][1]=instruction;
	instr_Table_Array[instr_addr][2]=operand_address;
	instr_addr++;
}
//back patch a previous instruction's address to a giving address
void back_patch (string jump_addr)
{
    string addr_string;
    int addr_int;
    addr_string = pop_jumpstack();
    //convert string to int:
    addr_int = atoi(addr_string.c_str());
    //change "operand_address" from -999 to 22 or whatever:
	instr_Table_Array[addr_int][2] = jump_addr;     
}
//this is a special back_patch for our if/else statements.
//it back patches a given previous instruction's address
//with a given address.
void back_patch_special (string jump_addr, string addr_string)
{
    int addr_int;
    //convert string to int:
    addr_int = atoi(addr_string.c_str());
    //change "operand_address" from -999 to 22 or whatever:
	instr_Table_Array[addr_int][2] = jump_addr;     
}
//print out the instruction Table
void print_out_InstrTable()
{
    /*
    Address         instruction             operand_address
    1                       PUSHM                   101
    2                       MUL                             -999
    3                       POPM                    104
    */
    //output file to print to: resultsFile_Asmb
    cout<<"INSTRUCTION TABLE: "<<endl;
    resultsFile_Asmb<<"INSTRUCTION TABLE: "<<endl;
    cout<<setw(18)<<left<<"Address"<<setw(18)<<left<<"Instruction"<<setw(18)<<left<<"Operand Address"<<endl;
    resultsFile_Asmb<<setw(18)<<left<<"Address"<<setw(18)<<left<<"instruction"<<setw(18)<<left<<"operand_address"<<endl;
    cout<<"------------------------------------------------------"<<endl;
    resultsFile_Asmb<<"------------------------------------------------------"<<endl;
    //Print out all identifiers in the table
    for(int y=1; y<=instr_addr; y++)
    {
            cout<<setw(18)<<left<<instr_Table_Array[y][0]<<setw(18)<<left<<instr_Table_Array[y][1]<<setw(18)<<left<<instr_Table_Array[y][2]<<endl;
            resultsFile_Asmb<<setw(18)<<left<<instr_Table_Array[y][0]<<setw(18)<<left<<instr_Table_Array[y][1]<<setw(18)<<left<<instr_Table_Array[y][2]<<endl;
    }
}
//get next (current) instruction address
string get_current_inst_address()
{
    //convert current instruction number (instr_addr)
    //into a string
    char buf3[3];
    instr_addr_string = itoa(instr_addr, buf3, 10);
	return instr_addr_string;
}

//*****JumpStack Actions:*****
//pushes an instruction # onto the jumpstack
//to use later in back_patch
void push_jumpstack(string instr_address)
{
	jumpStack.push(instr_address);  
}

//pop the jumpstack to get an instruction #.
//used in back_patch.
string pop_jumpstack()
{
    string top_of_jumpstack;

    top_of_jumpstack = jumpStack.top();
    jumpStack.pop();
    return top_of_jumpstack;
}

#endif